﻿///*
// * SRSTransformLibrary
// * 
// * initial implementation : 20.11.2008
// *
// * Copyright (c) Adam Sida, asida@centrum.cz.
// * All rights reserved.
// * 
// * for license see Petra.Gis.License.txt
// */
//using System;
//using System.Collections.Generic;
//using System.Runtime.Serialization;
//using Petra.Gis.Support;
//using Petra.Gis.ReferenceSystemServices;
//using Petra.Gis.ReferenceSystemServices.Transformations;


//namespace Petra.Gis
//{

//  /// <summary>
//  /// also represents transformation search engine
//  /// that searches in <c>CoordinateSystemTransformCache</c>
//  /// for existing transform.
//  /// </summary>
//  public partial class SRSTransformLibrary
//  {
//    private CoordinateSystem _rSourceCS; //for recursive search
//    private CoordinateSystem _rTargetCS; //for recursive search

//    private CSTPNode root;
//    private List<CSTPNode> _successes;


//    //private static CoordinateSystemTransformCache _cstCache;

//    //public static TransformList Transforms;




//    #region public Members




//    /// <summary>
//    /// Creates a transformation between two coordinate systems.
//    /// </summary>
//    /// <remarks>
//    /// This method examines the coordinate systems in order to construct
//    /// a transformation between them. This method may fail if no path between 
//    /// the coordinate systems is found, using the normal failing behavior of 
//    /// the DCP (e.g. throwing an exception).</remarks>
//    /// <param name="sourceCS">Source coordinate system</param>
//    /// <param name="targetCS">Target coordinate system</param>
//    /// <returns>transformation between source and target coordinate system,</returns>	
//    public SRSTransform SearchTransformationBy(CoordinateSystem sourceCS, CoordinateSystem targetCS)
//    {

//      // search loaded cache for transform...
//      TransformList result = _byCoordinateSystems(sourceCS, targetCS);

//      if (result == null)
//        //throw new ArgumentException("transformation not found");
//        return null;

//      if (result.Count == 1)
//        return result[0];
//      else
//        return new ChainedTransform(sourceCS, targetCS, result);

//    }


//    #endregion






//    #region private members


//    private TransformList _byCoordinateSystems(CoordinateSystem sourceCS, CoordinateSystem targetCS)
//    {
//      TransformList result;
//      //TransformList result = new TransformList();
//      //result = new TransformList();

//      // function starts to search from simple case to complex case
//      // to avoid cpu time consumption for transforms

//      // so first test whether sourceCS and targetCS are same
//      // if yes, no transform needed at all (VoidTransform)
//      // or only storage unit transform
//      if (sourceCS.EqualParams(targetCS, CsCompareType.SkipStorageUnits))
//      {
//        result = new TransformList();

//        if (sourceCS.EqualParams(targetCS, CsCompareType.CompareAllParams))
//          result.Add(new VoidTransform(sourceCS, targetCS)); //both are exactly same
//        else //storage units transform
//          result.Add(new MeasureUnitTransform(sourceCS, targetCS)); //same but different units, provide storage units transform

//        return result;
//      }


//      // ok not same, may be units are different and x,y axes are flipped or different direction of axis ?
//      // if both coordinate systems are projected and has x,y axis
//      // if axes has different orientation try prepare Coordinate quadrant transform
//      // and also resolve units
//      if (sourceCS is ProjectedCoordinateSystem && targetCS is ProjectedCoordinateSystem)
//      {
//        if (sourceCS.EqualParams(targetCS, CsCompareType.SkipProjectionAxesOrientation | CsCompareType.SkipStorageUnits))
//        {
//          result = _resolveStorageUnitsAndAxes(sourceCS, targetCS);

//          //SRSTransform tmp = XYQuadrantTransform.Create(
//          //                      (ProjectedCoordinateSystem)sourceCS, 
//          //                      (ProjectedCoordinateSystem)targetCS);
//          if (result != null)
//            return result;
//        }
//      }


//      // ok, source and target are totaly different
//      // all next possibilities are more time consuming
//      // because requres math transforms and other non simple math equations
//      // now search for direct identical transform first...
//      result = searchDirectTransform(sourceCS, targetCS);
//      if (result != null)
//        return result;


//      // ok still not found,...
//      // if source CS is projected try search for projcs to its geogcs
//      // and geogcs to target directly
//      if (sourceCS is ProjectedCoordinateSystem && 
//          targetCS.EqualParams(((ProjectedCoordinateSystem)sourceCS).GeographicCoordinateSystem, CsCompareType.SkipStorageUnits))
//      {
//        // now search for projection to spheroid transform
//        //SRSTransform tmpTr = projectionToSpheroid((ProjectedCoordinateSystem)sourceCS);
//        //TransformList tmpTrSrc = directTransform(sourceCS, ((ProjectedCoordinateSystem)sourceCS).GeographicCoordinateSystem);

//        // this call will throw an exception when projection is unsupported by Petra.Gis
//        SRSTransform tmpTrSrc = ProjectionTransform.Create((ProjectedCoordinateSystem)sourceCS, false);

//        // if any exists try search spheroid to targetcs
//        if (tmpTrSrc != null)
//        {
//          //search direct transform again
//          result = searchDirectTransform(((ProjectedCoordinateSystem)sourceCS).GeographicCoordinateSystem, targetCS);
//          if (result != null)
//          {
//            // insert all transforms for source before result
//            if (tmpTrSrc is ChainedTransform)
//              foreach (SRSTransform t in ((ChainedTransform)tmpTrSrc).CoordinateTransformList)
//                result.Insert(0, t);

//            else
//              result.Insert(0, tmpTrSrc);

//            return result;
//          }
//        }
//      }


//      // or oposite situation, target is projcs and source is geogcs
//      if (targetCS is ProjectedCoordinateSystem &&
//        sourceCS.EqualParams(((ProjectedCoordinateSystem)sourceCS).GeographicCoordinateSystem, CsCompareType.SkipStorageUnits))
//      {
//        //SRSTransform tmpTr = projectionToSpheroid((ProjectedCoordinateSystem)targetCS);
//        //TransformList tmpTrTrg = directTransform(((ProjectedCoordinateSystem)targetCS).GeographicCoordinateSystem, targetCS);
//        SRSTransform tmpTrTrg = ProjectionTransform.Create((ProjectedCoordinateSystem)targetCS, true);

//        //if (tmpTrTrg != null)
//        if (tmpTrTrg != null)
//        {
//          //search direct transform again
//          result = searchDirectTransform(sourceCS, ((ProjectedCoordinateSystem)targetCS).GeographicCoordinateSystem);
//          if (result != null)
//          {
//            // add all transforms for target after result
//            if (tmpTrTrg is ChainedTransform)
//              foreach (SRSTransform t in ((ChainedTransform)tmpTrTrg).CoordinateTransformList)
//                result.Add(t);

//            else
//              result.Insert(0, tmpTrTrg);

//            return result;
//          }
//        }
//      }


//      // ok still not found
//      // try this:
//      // if source and target is projcs, find their projcs to geogcs transforms
//      // if both projections supported (no error) then search direct transform between their geogcs
//      // this is most common case when map display
//      if (sourceCS is ProjectedCoordinateSystem && targetCS is ProjectedCoordinateSystem)
//      {
//        //SRSTransform tmpTrSrc = projectionToSpheroid((ProjectedCoordinateSystem)sourceCS);
//        //SRSTransform tmpTrTrg = projectionToSpheroid((ProjectedCoordinateSystem)targetCS);
//        //TransformList tmpTrSrc = directTransform(sourceCS, ((ProjectedCoordinateSystem)sourceCS).GeographicCoordinateSystem);
//        //TransformList tmpTrTrg = directTransform(((ProjectedCoordinateSystem)targetCS).GeographicCoordinateSystem, targetCS);
//        SRSTransform tmpTrSrc = ProjectionTransform.Create((ProjectedCoordinateSystem)sourceCS, false);
//        SRSTransform tmpTrTrg = ProjectionTransform.Create((ProjectedCoordinateSystem)targetCS, true);

//        if (tmpTrSrc != null && tmpTrTrg != null)
//        {
//          //search direct transform again
//          result = searchDirectTransform(((ProjectedCoordinateSystem)sourceCS).GeographicCoordinateSystem, ((ProjectedCoordinateSystem)targetCS).GeographicCoordinateSystem);
//          if (result != null)
//          {
//            // insert all transforms for source before result
//            if (tmpTrSrc is ChainedTransform)
//              //foreach (SRSTransform t in ((ChainedTransform)tmpTrSrc).CoordinateTransformList)
//              //  result.Insert(0, t);
//              result.InsertRange(0,((ChainedTransform)tmpTrSrc).CoordinateTransformList);

//            else
//              result.Insert(0, tmpTrSrc);

//            // and all transforms for target after result
//            if (tmpTrTrg is ChainedTransform)
//              foreach (SRSTransform t in ((ChainedTransform)tmpTrTrg).CoordinateTransformList)
//                result.Add(t);

//            else
//              result.Add(tmpTrTrg);

//            return result;
//          }
//          //else
//          //  //no result
//          //  return null;
//        }
//      }


//      // damn,... ok, worst case, no simple way to find transform
//      // must try to use long recursive search....

//      //return null;
//      //throw new NotImplementedException("sorry transform between different CS is not supported now");

//      _successes = new List<CSTPNode>();

//      // _sourceCS & _targetCS are used as global vars
//      // for recurse calls
//      // flip coordinate systems
//      // this is because search engine returns transform path in reverse order
//      CoordinateSystem tmpCS = _rSourceCS;
//      _rSourceCS = targetCS;
//      _rTargetCS = tmpCS;

//      root = new CSTPNode(null, null);

//      //recursive search...
//      treeSearchTransform(root, null);

//      if (_successes.Count == 0)
//        return null;
//      else
//      {
//        //sort result by level, smallest level = less transforms in path
//        CSTPNode[] tmp = _successes.ToArray();
//        Array.Sort(tmp);

//        //put whole coord transf path to list
//        result = new TransformList();
//        CSTPNode tmp1 = tmp[0];
//        result.Add(tmp1.transform);
//        while (tmp1.Parent != null)
//        {
//          result.Add(tmp1.Parent.transform);
//          tmp1 = tmp1.Parent;
//        }

//        //check whether storage unist of start and end transform
//        //corresponds with source and target CS
//        if (result[0].Source.EqualParams(sourceCS, CsCompareType.CompareAllParams))
//          result.Insert(0, new MeasureUnitTransform(result[0].Source, sourceCS));

//        if (result[result.Count - 1].Target.EqualParams(targetCS, CsCompareType.CompareAllParams))
//          result.Insert(0, new MeasureUnitTransform(result[result.Count - 1].Target, targetCS));

//        return result;
//      }

//    }





//    /// <summary>
//    /// recursive search transform through whole database of transforms.
//    /// This function builds tree with as many subpaths as many possible transforms exists
//    /// serarch ends when correct transform is found or when there is nothing more 
//    /// to search througn (add to tree).
//    /// search goes in reverse order, so first cs is searched as target cs of transform
//    /// </summary>
//    /// <param name="current"></param>
//    /// <param name="currentCS"></param>
//    private void treeSearchTransform(CSTPNode current, CoordinateSystem currentCS)
//    {

//      if (currentCS != null)
//      {
//        if (currentCS == _rSourceCS)
//          return;
//      }
//      else
//        currentCS = _rSourceCS;


//      CSTPNode tmp;


//      // scan for currentCS
//      // compare target because search is in reverse order
//      foreach (SRSTransform t in this.Transformations)
//        if (t.Target.EqualParams(currentCS, CsCompareType.SkipStorageUnits) &&
//            !_csExistInTreeAsSource(current, t.Target))
//        {
//          tmp = new CSTPNode(current, t);
//          current.childs.Add(tmp);
//          // if is same as source cs then stop search at all for this tree path
//          if (t.Source.EqualParams(_rTargetCS, CsCompareType.SkipStorageUnits))
//          {
//            _successes.Add(tmp);
//            return;
//          }
//        }

//      // if any success in search
//      // and not found final transform
//      // recursive call....
//      if (current.childs.Count > 0)
//        foreach (CSTPNode n in current.childs)
//          treeSearchTransform(n, n.transform.Target);


//    }




//    /// <summary>
//    /// searches recursively all nodes up from current level
//    /// whether path contains searched coordinate system as source
//    /// this will avoid circular reference and unlimited loop
//    /// in search function
//    /// </summary>
//    /// <param name="node">start search node</param>
//    /// <param name="asSource">coordinate system to search for in source of transform</param>
//    /// <returns></returns>
//    private bool _csExistInTreeAsSource(CSTPNode node, CoordinateSystem asSource)
//    {
//      foreach (CSTPNode ch in node.childs)
//        if (ch.transform.Source.EqualParams(asSource, CsCompareType.SkipStorageUnits))
//          return true;

//      if (node.Parent != null)
//        return _csExistInTreeAsSource(node.Parent, asSource);

//      return false;
//    }






//    //private static SRSTransform projectionToSpheroid(ProjectedCoordinateSystem proj)
//    //{
//    //  SRSTransform transform;

//    //  switch (proj.Projection.ClassName.ToLower())
//    //  {
//    //    case "mercator_1sp":
//    //    case "mercator_2sp":
//    //      //1SP
//    //      transform = new MercatorProjection(proj);
//    //      break;
//    //    case "transverse_mercator":
//    //      transform = new TransverseMercatorProjection(proj);
//    //      break;
//    //    //case "albers":
//    //    //  transform = new AlbersProjectionTransform(proj);
//    //    //  break;
//    //    //case "lambert_conformal_conic":
//    //    //case "lambert_conformal_conic_2sp":
//    //    //  transform = new LambertConformalConic2SPTransform(proj);
//    //    //  break;
//    //    default:
//    //      //throw new NotSupportedException(String.Format("Projection {0} is not supported.", proj.Projection.ClassName));
//    //      return null;
//    //  }
//    //  return transform;
//    //}





//    /// <summary>
//    /// search in transform database for direct transform between source and target
//    /// </summary>
//    /// <param name="sourceCS">source coordinate system</param>
//    /// <param name="targetCS">target coordinate system</param>
//    /// <returns>if succes returns prepared TransformList, otherwise returns null</returns>
//    private TransformList searchDirectTransform(CoordinateSystem sourceCS, 
//                                                 CoordinateSystem targetCS)
//    {

//      TransformList result;

//      // go over all transform in database
//      // if current transform source and target cs is same to input ones
//      // get this as result
//      foreach (SRSTransform t in this.Transformations)
//        if (t.Source.EqualParams(sourceCS, CsCompareType.CompareAllParams) && 
//            t.Target.EqualParams(targetCS, CsCompareType.CompareAllParams))
//        {
//          result = new TransformList();
//          result.Add(t);
//          return result;
//        }
//        // or may be current transform is inversible and source and target are
//        // saved in database in reverse order
//        // so search also in reverse order ;-)
//        else if(t.IsInversible)
//        {
//          if (t.Source.EqualParams(targetCS, CsCompareType.CompareAllParams) &&
//              t.Target.EqualParams(sourceCS, CsCompareType.CompareAllParams))
//          {
//            result = new TransformList();
//            result.Add(t.Inverse());
//            return result;
//          }
//        }


//      // ok, not found
//      // try search for direct transform where storage units might differ...
//      foreach (SRSTransform t in this.Transformations)
//        if (t.Source.EqualParams(sourceCS, CsCompareType.SkipStorageUnits) && 
//            t.Target.EqualParams(targetCS, CsCompareType.SkipStorageUnits))
//        {
//          result = new TransformList();

//          // if source units and transform.source units are different
//          // add MeasureUnitTransform source to transform source
//          if (!t.Source.EqualParams(sourceCS, CsCompareType.CompareAllParams)) //storage units differs
//            result.Add(new MeasureUnitTransform(sourceCS, t.Source));

//          // add transform itself
//          result.Add(t);

//          // if transform.target units and target units are different
//          // add MeasureUnitTransform transform.target to target
//          if (!t.Target.EqualParams(targetCS, CsCompareType.CompareAllParams)) //storage units differs
//            result.Add(new MeasureUnitTransform(t.Target, targetCS));

//          return result;
//        }

//      // ok, still not found
//      // search for direct transform where units and axes might differ
//      // try search for direct transform where storage units might differ
//      // and axes has different orientation
//      foreach (SRSTransform t in this.Transformations)
//      {
//        if (t.Source.EqualParams(sourceCS, CsCompareType.SkipStorageUnits | CsCompareType.SkipProjectionAxesOrientation) &&
//            t.Target.EqualParams(targetCS, CsCompareType.SkipStorageUnits | CsCompareType.SkipProjectionAxesOrientation))
//        {
//          return _resolveStorageUnitsAndAxes(t, sourceCS, targetCS);
//        }
//        else if (t.IsInversible)
//        {
//          if (t.Source.EqualParams(targetCS, CsCompareType.SkipStorageUnits | CsCompareType.SkipProjectionAxesOrientation) &&
//              t.Target.EqualParams(sourceCS, CsCompareType.SkipStorageUnits | CsCompareType.SkipProjectionAxesOrientation))
//          {
//            return _resolveStorageUnitsAndAxes(t, targetCS, sourceCS);
//          }
//        }
//      }

//      return null;
//    }




//    /// <summary>
//    /// if input transform matches input coordinate systems except storage units and axis def.
//    /// this function tries to prepare MeasureUnitTransform to convert units
//    /// and also if one or both coordinate systems are projcs, prepares XYQuadrantTransform
//    /// for projection
//    /// </summary>
//    /// <param name="t">transform to compare with</param>
//    /// <param name="sourceCS">source coordinate system</param>
//    /// <param name="targetCS"> target coordinate system</param>
//    /// <returns>TransformList with MeasureUnitTransform and or XYQuadrantTransform and input transform or null</returns>
//    private TransformList _resolveStorageUnitsAndAxes(SRSTransform t, CoordinateSystem sourceCS, CoordinateSystem targetCS)
//    {
//      if (t.Source.EqualParams(sourceCS, CsCompareType.SkipStorageUnits | CsCompareType.SkipProjectionAxesOrientation) &&
//          t.Target.EqualParams(targetCS, CsCompareType.SkipStorageUnits | CsCompareType.SkipProjectionAxesOrientation))
//      {

//        TransformList result = new TransformList();

//        // if source units and transform.source units are different
//        // add MeasureUnitTransform source to transform source
//        if (!t.Source.EqualParams(sourceCS, CsCompareType.SkipProjectionAxesOrientation))
//          result.Add(new MeasureUnitTransform(sourceCS, t.Source));

//        // if source and transform.source has different axis definition
//        // and both are projection
//        if (!t.Source.EqualParams(sourceCS, CsCompareType.SkipStorageUnits) &&
//            t.Source is ProjectedCoordinateSystem)
//        {
//          // try if XYQuadrantTransform is possible
//          SRSTransform tmp = XYQuadrantTransform.Create((ProjectedCoordinateSystem)sourceCS, (ProjectedCoordinateSystem)t.Source);
//          if (tmp != null)
//            result.Add(tmp);
//          else
//            return null;
//        }

//        // add transform itself
//        result.Add(t);


//        // if source and transform.source has different axis definition
//        // and both are projection
//        if (!t.Target.EqualParams(targetCS, CsCompareType.SkipStorageUnits) &&
//            t.Target is ProjectedCoordinateSystem)
//        {
//          // try if XYQuadrantTransform is possible
//          SRSTransform tmp = XYQuadrantTransform.Create((ProjectedCoordinateSystem)t.Target, (ProjectedCoordinateSystem)targetCS);
//          if (tmp != null)
//            result.Add(tmp);
//          else
//            return null;
//        }

//        // if transform.target units and target units are different
//        // add MeasureUnitTransform transform.target to target
//        if (!t.Target.EqualParams(targetCS, CsCompareType.SkipProjectionAxesOrientation))
//          result.Add(new MeasureUnitTransform(t.Target, targetCS));

//        return result;
//      }

//      return null;
//    }







//    /// <summary>
//    /// if sourceCS matches targetCS except storage units and axis def.
//    /// this function tries to prepare MeasureUnitTransform to convert units
//    /// and also if both coordinate systems are projcs, prepares XYQuadrantTransform
//    /// for projection. 
//    /// </summary>
//    /// <param name="sourceCS">source coordinate system</param>
//    /// <param name="targetCS"> target coordinate system</param>
//    /// <returns>TransformList with MeasureUnitTransform and or XYQuadrantTransform or null</returns>
//    private TransformList _resolveStorageUnitsAndAxes(CoordinateSystem sourceCS, CoordinateSystem targetCS)
//    {
//      if (sourceCS.EqualParams(targetCS, CsCompareType.SkipStorageUnits | CsCompareType.SkipProjectionAxesOrientation))
//      {

//        TransformList result = new TransformList();

//        // if sourceCS units and targetCS units are different
//        // add MeasureUnitTransform sourceCS to targetCS
//        if (!sourceCS.EqualParams(targetCS, CsCompareType.SkipProjectionAxesOrientation))
//          result.Add(new MeasureUnitTransform(sourceCS, targetCS));

//        // if sourceCS and targetCS has different axis definition
//        // and both are projection
//        if (!targetCS.EqualParams(sourceCS, CsCompareType.SkipStorageUnits) &&
//            targetCS is ProjectedCoordinateSystem && sourceCS is ProjectedCoordinateSystem)
//        {
//          // try if XYQuadrantTransform is possible
//          SRSTransform tmp = XYQuadrantTransform.Create((ProjectedCoordinateSystem)sourceCS, (ProjectedCoordinateSystem)targetCS);
//          if (tmp != null)
//            result.Add(tmp);
//          else
//            return null;
//        }

//        return result;

//      }

//      return null;
//    }
    

//    #endregion private members







//  }
//}
